//@author A0105735J
#include <time.h>
#include <assert.h>
#include "Receiver.h"
Receiver::Receiver(void){
	searchID = 1;
	storage = new Storage;
}

Receiver::Receiver(Storage* store){
	searchID = 1;
	storage = store;
}

Receiver::~Receiver(void){
	delete storage;
}

Feedback Receiver::invalidCommand(Error errorMessage){
	Logger::instance()->LogMessage("trying to write feedback for an invalid user input");	
	if(errorMessage == ErrorFormat){
		return writefailureFeedback(ErrorFormat);
	}else{
		return writefailureFeedback(errorMessage);
	}
}

Feedback Receiver::addCommand(Event* inputEvent){
	tempEvents.push_back(inputEvent);
	inputEvent->setSearchID(searchID);
	searchID++;
	Logger::instance()->LogMessage("trying to reset id in calendar");	
	setEventsID(tempEvents,1);
	writeFile();

	std::vector<Event*> operatedEvents;
	operatedEvents.push_back(inputEvent);
	return writeSucceedFeedback(operatedEvents, SucceedAdd);
}

Feedback Receiver::deleteCommand(std::vector<int> id){
	std::vector<Event*>::iterator iter;
	std::vector<Event*> operatedEvents;
	int succedDelete = 0;
	int targetDelete = id.size();
	// analyzer ensure that id could not be empty
	assert(targetDelete > 0);
	
	while(!id.empty()){
		for (iter = tempEvents.begin(); iter < tempEvents.end();){
			if((*iter)->getDisplayID() == id.back()){
				operatedEvents.push_back(*iter);
				Logger::instance()->LogMessage("trying to reset id in calendar");	
				iter = tempEvents.erase(iter);
				succedDelete++;
			}else{
				iter++;
			}
		}
		id.pop_back();
	}
	setEventsID(tempEvents,1);
	writeFile();
	
	// check if all the display ID are found
	if(succedDelete == targetDelete){
		Logger::instance()->LogMessage("trying to write feedback for succeeding to delete the corresponding events");
		return writeSucceedFeedback(operatedEvents, SucceedDelete);
	}else{
		Logger::instance()->LogMessage("trying to write feedback for not finding some id");
		return writefailureFeedback(ErrorNotFound);
	}
}

Feedback Receiver::updateCommand(int id, Event* newEvent){
	std::vector<Event*>::iterator iter;
	// analyzer assure the id could not be 0
	assert(id >= 0);
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		// check if the display ID is found
		if((*iter)->getDisplayID() == id){
			int oldID = (*iter)->getSearchID();
			Event* oldEvent = getTargetEvent(id,1);
			Event* outputEvent = updateTargetEvent(newEvent, oldEvent);
			outputEvent->setStatus(PENDING);
			
			std::vector<int> deleteID;
			deleteID.push_back(id);
			deleteCommand(deleteID);
			addCommand(outputEvent);
			outputEvent->setSearchID(oldID);
			
			Logger::instance()->LogMessage("trying to reset id in calendar");	
			setEventsID(tempEvents,1);
			writeFile();

			std::vector<Event*> operatedEvents;
			operatedEvents.push_back(outputEvent);
			Logger::instance()->LogMessage("trying to write feedback for succeeding to update the event");
			return writeSucceedFeedback(operatedEvents,SucceedUpdate);
		}
	}
	Logger::instance()->LogMessage("trying to write feedback for not finding id");
	return writefailureFeedback(ErrorNotFound);
}

Event* Receiver::updateTargetEvent(Event* newEvent, Event* oldEvent){	
	Logger::instance()->LogMessage("trying to update the event");
	Event* outputEvent = updateEventType(newEvent, oldEvent);
	 
	copyEventInfo(outputEvent, oldEvent);	
	
	// if the user does not want to update some attribute, then Analyzer will set this attributes as "\n"
	if(newEvent->getImportanceLevel() != "\n"){
		outputEvent->setImportanceLevel(newEvent->getImportanceLevel());	
	}
	if(newEvent->getLocation() != "\n"){
		outputEvent->setLocation(newEvent->getLocation());
	}
	if(newEvent->getName() != "\n"){
		outputEvent->setName(newEvent->getName());
	}	
	
	if(outputEvent->getEventType() == TIME){
		DateTime startTime = newEvent->getTimedEventStartTime();
		DateTime endTime = newEvent->getTimedEventEndTime();
		if(!startTime.isEmpty()){
			outputEvent->setTimedEventStartTime(startTime);
		}
		if(!endTime.isEmpty()){
			outputEvent->setTimedEventEndTime(endTime);
		}
	}else if(outputEvent->getEventType() == DEADLINE){
		DateTime endTime = newEvent->getDeadlineEventEndTime();
		if(!endTime.isEmpty()){
			outputEvent->setDeadlineEventEndtime(endTime);
		}
	}	
	return outputEvent;
}

Event* Receiver::updateEventType(Event* newEvent, Event* oldEvent){
	Logger::instance()->LogMessage("trying to build a new event with the desired type");
	if(newEvent->getEventType() == DEADLINE){
		DeadlineEvent* updatedDeadlineEvent = new DeadlineEvent; 
		return updatedDeadlineEvent;
	}else if(newEvent->getEventType() == TIME){
		TimedEvent* updatedTimedEvent = new TimedEvent; 
		return updatedTimedEvent;
	}else if(newEvent->getEventType() == FLOATING){
		FloatingEvent* updatedFloatingEvent = new FloatingEvent;
		return updatedFloatingEvent;
	}else{
		// In this case, the newEvent has the type SameEvent, which indicates the oldEvent and newEvent has the same type
		// Then just return the oldEVent, which has the desired type
		return oldEvent;
	}
}

void Receiver::copyEventInfo(Event* outputEvent, Event* oldEvent){
	Logger::instance()->LogMessage("In the update process, trying to copy the old event's information to new event");
	outputEvent->setImportanceLevel(oldEvent->getImportanceLevel());	
	outputEvent->setLocation(oldEvent->getLocation());
	outputEvent->setName(oldEvent->getName());
	if(outputEvent->getEventType() == oldEvent->getEventType()){
		if(outputEvent->getEventType() == TIME){
			DateTime startTime = oldEvent->getTimedEventStartTime();
			DateTime endTime = oldEvent->getTimedEventEndTime();
			outputEvent->setTimedEventStartTime(startTime);
			outputEvent->setTimedEventEndTime(endTime);

		}else if(outputEvent->getEventType() == DEADLINE){
			DateTime endTime = oldEvent->getDeadlineEventEndTime();
			outputEvent->setDeadlineEventEndtime(endTime);
		}
	}
}

Feedback Receiver::markCommand(std::vector<int> id, std::string status){
	Logger::instance()->LogMessage("trying to mark some events as done");
	std::vector<Event*>::iterator iter;
	std::vector<Event*> operatedEvents;
	int succeedMark = 0;
	int targetMark = id.size();
	assert(targetMark > 0);
	while(!id.empty()){
		for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
			if((*iter)->getDisplayID() == id.back()){
				(*iter)->setStatus(status);
				operatedEvents.push_back(*iter);
				succeedMark++;
			}
		}
		id.pop_back();
	}
	writeFile();

	// check if all the display ID are found
	if(succeedMark == targetMark){
		Logger::instance()->LogMessage("trying to write feedback for succeeding to mark the status of events");
		return writeSucceedFeedback(operatedEvents, SucceedMark);
	}else{
		Logger::instance()->LogMessage("trying to write feedback for not finding some id");
		return writefailureFeedback(ErrorNotFound);
	}
}

Feedback Receiver::checkCommand(){
	Logger::instance()->LogMessage("trying to update events' status as over due based on current time");
	DateTime current = getCurrentTime();

	std::vector<Event*>::iterator iter;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		if((*iter)->getEventType()!=FLOATING){
			if((*iter)->getEventType() == TIME){
				if(beforeTime((*iter)->getTimedEventEndTime(),current)){
					if((*iter)->getEventStatus()!=DONE){
						(*iter)->setStatus(OVERDUE);
					}
				}
			}else{
				if((*iter)->getEventType() == DEADLINE){
					if(beforeTime((*iter)->getDeadlineEventEndTime(),current)){
						if((*iter)->getEventStatus()!=DONE){
							(*iter)->setStatus(OVERDUE);
						}
					}
				}
			}
		}
	}
	std::vector<Event*> overdueEvent;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		if((*iter)->getEventStatus() == OVERDUE){
			overdueEvent.push_back(*iter);
		}
	}
	writeFile();
	return writeSucceedFeedback(overdueEvent, SucceedStatus);
}

DateTime Receiver::getCurrentTime(){
	time_t tt = time(NULL);
	tm* t= localtime(&tt);
	DateTime current;
	Logger::instance()->LogMessage("trying to find current time");
	current.setDate(t->tm_mday, t->tm_mon + 1,t->tm_year + 1900);
	current.setTime(t->tm_hour, t->tm_min);
	return current;
}

Feedback Receiver::searchCommand(std::string searchTarget){
	std::vector<Event*>::iterator iter;
	std::vector<Event*> searchResult;
	assert(!searchTarget.empty());
	
	std::size_t foundName;
	std::size_t foundLocation;
	std::size_t foundImportance;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		foundName = (*iter)->getName().find(searchTarget);
		foundLocation = (*iter)->getLocation().find(searchTarget);
		foundImportance = (*iter)->getImportanceLevel().find(searchTarget);
		
		if(foundName != std::string::npos||foundLocation != std::string::npos ||foundImportance != std::string::npos){
			searchResult.push_back(*iter);
		}	
	}
	
	if(searchResult.empty()){
		Logger::instance()->LogMessage("trying to write feedback for not finding the keywords");
		return writefailureFeedback(ErrorNotFound);
	}
	else{
		clearID();
		Logger::instance()->LogMessage("trying to reset id in search result");	
		setEventsID(searchResult,1);
		std::vector<Event*> restEvents;
		for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
			if((*iter)->getDisplayID() == 0){
				restEvents.push_back(*iter);
			}
		}
		Logger::instance()->LogMessage("trying to reset id in non-search result");	
		setEventsID(restEvents,searchResult.size()+1);
		Logger::instance()->LogMessage("trying to write feedback for succeeding find the keywords");
		return writeSucceedFeedback(searchResult, SucceedSearch);
	}
}

Feedback Receiver::displayCommand(DateTime startTime, DateTime endTime){
	Logger::instance()->LogMessage("trying to display events");
	DateTime temp;
	std::vector<Event*>::iterator iter;
	std::vector<Event*> displayList;
	assert(beforeDate(startTime,endTime));
	
	// This is the case that start time equal end time, which means the user wants to display the events in the single day
	// find events in this day
	if(equalDate(startTime, endTime)){
		for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
			if((*iter)->getEventType() == TIME){
				temp = (*iter)->getTimedEventStartTime();
			}else if((*iter)->getEventType() == DEADLINE){
				temp = (*iter)->getDeadlineEventEndTime();
			}
			else{
				temp = startTime;
			}
			if(equalDate(temp,startTime)){
				displayList.push_back(*iter);
			}
		}
	}else{
		for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
			if((*iter)->getEventType() == TIME){
				temp = (*iter)->getTimedEventStartTime();
			}else if((*iter)->getEventType() == DEADLINE){
				temp = (*iter)->getDeadlineEventEndTime();
			}
			else{
				temp = startTime;
			}		
			
			if(beforeDate(startTime, temp) && beforeDate(temp, endTime)){
				displayList.push_back(*iter);
			}
		}
	}
	clearID();
	setEventsID(displayList,1);
	std::vector<Event*> restEvents;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		if((*iter)->getDisplayID() == 0){
			restEvents.push_back(*iter);
		}
	}
	setEventsID(restEvents,displayList.size()+1);
	return writeSucceedFeedback(displayList,SucceedDisplay);
}

bool Receiver::equalDate(DateTime startTime, DateTime endTime){
	return ((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() == endTime.getMonth())&&(startTime.getDay() == endTime.getDay()));
}

bool Receiver::beforeDate(DateTime startTime, DateTime endTime){
	if((startTime.getYear() < endTime.getYear())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() < endTime.getMonth())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() == endTime.getMonth())&&(startTime.getDay() <= endTime.getDay())){
		return true;
	}else{
		return false;
	}	
}

bool Receiver::beforeTime(DateTime startTime, DateTime endTime){
	if((startTime.getYear() < endTime.getYear())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() < endTime.getMonth())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() == endTime.getMonth())&&(startTime.getDay() < endTime.getDay())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() == endTime.getMonth())&&(startTime.getDay() == endTime.getDay())&&(startTime.getHour() < endTime.getHour())){
		return true;
	}else if((startTime.getYear() == endTime.getYear())&&(startTime.getMonth() == endTime.getMonth())&&(startTime.getDay() == endTime.getDay())&&(startTime.getHour() == endTime.getHour())&&(startTime.getMin() < endTime.getMin())){
		return true;
	}else{
		return false;
	}
}


void Receiver::clearID(){
	std::vector<Event*>::iterator iter;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		(*iter)->setDisplayID(0);
	}
}

Event* Receiver::getTargetEvent(int ID, int mode){
	std::vector<Event*>::iterator iter;
	for (iter = tempEvents.begin(); iter < tempEvents.end(); iter++){
		// there are two mode
		// mode 1 is for display ID
		// mode 2 is for search ID
		if(mode == 1){		
			if((*iter)->getDisplayID() == ID){
				return *iter;
			}
		}
		else{
			if((*iter)->getSearchID() == ID){
				return *iter;
			}
		}
	}
	Event* invlaidEvent;
	return invlaidEvent;
}

void Receiver::setEventsID(std::vector<Event*>& setEvents, int i){		
	std::vector<TimedEvent*> tempTimedEvents;
	std::vector<DeadlineEvent*> tempDeadlineEvents;
	std::vector<FloatingEvent*> tempFloatingEvents;
	Event* tempEvent;
	TimedEvent* tempTimedEvent;
	DeadlineEvent* tempDeadlineEvent;
	FloatingEvent* tempFloatingEvent;

	std::vector<Event*>::iterator iter;
	for (iter = setEvents.begin(); iter < setEvents.end(); iter++){
		if((*iter)->getEventType() == TIME){
			tempEvent = *iter;
			tempTimedEvent = (TimedEvent*) tempEvent;
			tempTimedEvents.push_back(tempTimedEvent);
		}
	}

	for (iter = setEvents.begin(); iter < setEvents.end(); iter++){
		if((*iter)->getEventType() == DEADLINE){
			tempEvent = *iter;
			tempDeadlineEvent = (DeadlineEvent*) tempEvent;
			tempDeadlineEvents.push_back(tempDeadlineEvent);
		}
	}

	for (iter = setEvents.begin(); iter < setEvents.end(); iter++){
		if((*iter)->getEventType() == FLOATING){
			tempEvent = *iter;
			tempFloatingEvent = (FloatingEvent*) tempEvent;
			tempFloatingEvents.push_back(tempFloatingEvent);
		}
	}

	// sort all three types of events
	std::sort(tempTimedEvents.begin(), tempTimedEvents.end(), rankByStartTime);
	std::sort(tempDeadlineEvents.begin(), tempDeadlineEvents.end(), rankByEndTime);
	std::sort(tempFloatingEvents.begin(), tempFloatingEvents.end(), rankByStatus);

	// reset all the display ID in each category
	std::vector<TimedEvent*>::iterator iterT;
	for (iterT = tempTimedEvents.begin(); iterT < tempTimedEvents.end(); iterT++, i++){
		(*iterT)->setDisplayID(i);
	}
	std::vector<DeadlineEvent*>::iterator iterD;
	for (iterD = tempDeadlineEvents.begin(); iterD < tempDeadlineEvents.end(); iterD++, i++){
		(*iterD)->setDisplayID(i);
	}
	std::vector<FloatingEvent*>::iterator iterF;
	for (iterF = tempFloatingEvents.begin(); iterF < tempFloatingEvents.end(); iterF++, i++){
		(*iterF)->setDisplayID(i);
	}
	
	// sort all events based on display ID
	sort(setEvents.begin(),setEvents.end(),rankByID);	
}

Feedback Receiver::readFileCommand(){
	tempEvents= storage->readFile();
	return writeSucceedFeedback(tempEvents, SuccedRead);
}

void Receiver::writeFile(){
	storage->writeFile(tempEvents);
}

Feedback Receiver::writeSucceedFeedback(std::vector<Event*> outputEvents, Succed succeedType){
	Feedback output;
	output.setSucceedType(succeedType);
	output.storeUpdateCalendar(outputEvents);
	output.setIsValid(true);
	return output;
}

Feedback Receiver::writefailureFeedback(Error error){
	Feedback output;
	output.setIsValid(false);
	output.setErrorType(error);
	return output;
}

std::vector<Event*> Receiver::getTempEvents(){
	return tempEvents;
}